/*
	This file is part of the OdinMS Maple Story Server
    Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc> 
		       Matthias Butz <matze@odinms.de>
		       Jan Christian Meyer <vimes@odinms.de>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License version 3
    as published by the Free Software Foundation. You may not use, modify
    or distribute this program under any other version of the
    GNU Affero General Public License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package net.sf.odinms.net.channel.handler;

import java.awt.Rectangle;
import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
import java.net.InetAddress;

import net.sf.odinms.client.MapleChalkboard; 
import net.sf.odinms.client.ExpTable;
import net.sf.odinms.client.ISkill;
import net.sf.odinms.client.MapleCharacter;
import net.sf.odinms.client.MapleClient;
import net.sf.odinms.client.MapleInventoryType;
import net.sf.odinms.client.MaplePet;
import net.sf.odinms.client.MapleStat;
import net.sf.odinms.client.SkillFactory;
import net.sf.odinms.net.AbstractMaplePacketHandler;
import net.sf.odinms.net.channel.ChannelServer;
import net.sf.odinms.net.world.remote.WorldLocation;
import net.sf.odinms.net.MaplePacket;
import net.sf.odinms.net.channel.remote.ChannelWorldInterface;
import net.sf.odinms.net.world.WorldRegistryImpl;
import net.sf.odinms.server.MapleInventoryManipulator;
import net.sf.odinms.server.MapleTrade;
import net.sf.odinms.tools.MaplePacketCreator;
import net.sf.odinms.tools.data.input.SeekableLittleEndianAccessor;
import net.sf.odinms.server.MapleItemInformationProvider;
import net.sf.odinms.server.maps.MapleMap;
import net.sf.odinms.server.MapleStatEffect; 
import net.sf.odinms.server.maps.MapleMist; 

import net.sf.odinms.tools.Pair;

public class UseCashItemHandler extends AbstractMaplePacketHandler {
    private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(UseCashItemHandler.class);

	@Override
	public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
        //log.info(slea.toString());//debugging purposes
		MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
		byte mode = slea.readByte();
		slea.readByte();
		int itemId = slea.readInt();
		int itemType = itemId/10000;
        ChannelServer cserv = c.getChannelServer();
               
        int jobid = c.getPlayer().getJob().getId()/100;
        ISkill improvingMaxHP = SkillFactory.getSkill(1000001);
        ISkill improvingMaxMP = SkillFactory.getSkill(2000001);
        int improvingMaxHPLevel = c.getPlayer().getSkillLevel(improvingMaxHP);
        int improvingMaxMPLevel = c.getPlayer().getSkillLevel(improvingMaxMP);
        int HPSkillAdd = 0;
        int MPSkillAdd = 0;
        if (improvingMaxHPLevel > 0)
        HPSkillAdd= improvingMaxHP.getEffect(improvingMaxHPLevel).getY();
        if (improvingMaxMPLevel > 0)
        MPSkillAdd = improvingMaxMP.getEffect(improvingMaxMPLevel).getY();
        int addmp = 0;
        int add = 0;
        int hpmin = 0;
        int mpmin = 0;
        if (jobid == 0) {
            add = (int) Math.floor(Math.random()*4+8);
            addmp = (int) Math.floor(Math.random()*2+6);
            hpmin = 12;
            mpmin = 8;
        }
        if (jobid == 1) {
            add = (int) Math.floor(Math.random()*4+20);
            addmp = (int) Math.floor(Math.random()*2+2);
            hpmin = 24 + HPSkillAdd;
            mpmin = 4;
        }
        if (jobid == 2) {
            add = (int) Math.floor(Math.random()*4+6);
            addmp = (int) Math.floor(Math.random()*2+18) + (MPSkillAdd*2);
            hpmin = 10;
            mpmin = 20 + (MPSkillAdd*2);
        }
        if (jobid == 3 || jobid == 5 || jobid == 9) {
            add = (int) Math.floor(Math.random()*4+16);
            addmp = (int) Math.floor(Math.random()*2+10);
            hpmin = 20;
            mpmin = 12;
        }
        if (jobid == 4) {
            add = (int) Math.floor(Math.random()*4+20);
            addmp = (int) Math.floor(Math.random()*2+10);
            hpmin = 20;
            mpmin = 12;
        }
               
        try {			
            if (itemType == 507){
				int megaType = itemId / 1000 % 10;
				if (megaType == 2) {
					c.getChannelServer().getWorldInterface().broadcastMessage(null, MaplePacketCreator.serverNotice(3, c.getChannel(), c.getPlayer().getName() + " : " + slea.readMapleAsciiString()).getBytes());
				}
				MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
			} else if (itemType == 539) {
				List<String> lines = new LinkedList<String>();
				for (int i = 0; i < 4; i++) {
					lines.add(slea.readMapleAsciiString());
				}
				c.getChannelServer().getWorldInterface().broadcastMessage(null, MaplePacketCreator.getAvatarMega(c.getPlayer(), c.getChannel(), itemId, lines, (slea.readByte() != 0)).getBytes());
				MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
			} else if(itemType == 530) {                                
                ii.getItemEffect(itemId).applyTo(c.getPlayer());
                MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
            } else if (itemType == 512) {
				c.getPlayer().getMap().startMapEffect(ii.getMsg(itemId).replaceFirst("%s", c.getPlayer().getName()).replaceFirst("%s", slea.readMapleAsciiString()), itemId);
				MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
            } else if (itemType == 510) {
                c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.musicChange("Jukebox/Congratulation"));
                MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
			} else if (itemType == 524) {
				MaplePet pet = c.getPlayer().getPet(0);
				if (pet == null) {
					c.getSession().write(MaplePacketCreator.enableActions());
					return;
				}
				if (!pet.canConsume(itemId)) {
					pet = c.getPlayer().getPet(1);
					if (pet != null) {
						if (!pet.canConsume(itemId)) {
							pet = c.getPlayer().getPet(2);
							if (pet != null) {
								if (!pet.canConsume(itemId)) {
									c.getSession().write(MaplePacketCreator.enableActions());
									return;
								}
							} else {
								c.getSession().write(MaplePacketCreator.enableActions());
								return;
							}
						}
					} else {
						c.getSession().write(MaplePacketCreator.enableActions());
						return;
					}
				}
				pet.setFullness(100);
				int closeGain = 100 * c.getChannelServer().getPetExpRate();
				if (pet.getCloseness() < 30000) {
					if (pet.getCloseness() + closeGain > 30000) {
						pet.setCloseness(30000);
					} else {
						pet.setCloseness(pet.getCloseness() + closeGain);
					}
					while (pet.getCloseness() >= ExpTable.getClosenessNeededForLevel(pet.getLevel()+1)) {
						pet.setLevel(pet.getLevel()+1);
						c.getSession().write(MaplePacketCreator.showOwnPetLevelUp(c.getPlayer().getPetIndex(pet)));
						c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.showPetLevelUp(c.getPlayer(), c.getPlayer().getPetIndex(pet)));
					}
				}
				c.getSession().write(MaplePacketCreator.updatePet(pet, true));
				c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.commandResponse(c.getPlayer().getId(), (byte) 1, 0, true, true), true);

				MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
			} else if (itemType == 517) {
				// 49 00 24 00 50 E3 4E 00 05 00 4A 61 7A 7A 79 2B A3 41 01
				MaplePet pet = c.getPlayer().getPet(0);
				if (pet == null) {
					c.getSession().write(MaplePacketCreator.enableActions());
					return;
				}
				String newName = slea.readMapleAsciiString();
				pet.setName(newName);
				c.getSession().write(MaplePacketCreator.updatePet(pet, true));
				c.getSession().write(MaplePacketCreator.enableActions());
				c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.changePetName(c.getPlayer(), newName, 1), true);
				MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
			} else if (itemType == 509) {
				// 49 00 04 00 D0 AA 4D 00 | 05 00 46 65 65 74 79 04 00 74 65 73 74 DA C6 C9 1D
				String sendTo = slea.readMapleAsciiString();
				String msg = slea.readMapleAsciiString();
				
				try {
					c.getPlayer().sendNote(sendTo, msg);
				} catch (SQLException e) {
					log.error("SAVING NOTE", e);
				}
				
				MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);			
            } else if (itemType == 520) {
                c.getPlayer().gainMeso(ii.getMeso(itemId), true, false, true);
                MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
                c.getSession().write(MaplePacketCreator.enableActions());
            } else if (itemType == 506) {
                log.info(slea.toString());
                slea.readShort();
                int unknown = slea.readInt();
                //MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
                c.getSession().write(MaplePacketCreator.enableActions());
            } else if (itemType == 505) { // ap reset
                if (itemId > 5050000) {
                    MapleCharacter player = c.getPlayer();
                    int SPTo = slea.readInt();
                    int SPFrom = slea.readInt();
                    ISkill skillSPTo = SkillFactory.getSkill(SPTo);
                    ISkill skillSPFrom = SkillFactory.getSkill(SPFrom);
                    int maxlevel = skillSPTo.getMaxLevel();
                    int curLevel = player.getSkillLevel(skillSPTo);
                    int curLevelSPFrom = player.getSkillLevel(skillSPFrom);
                    if ((curLevel + 1 <= maxlevel) && curLevelSPFrom > 0) {
                        player.changeSkillLevel(skillSPFrom, curLevelSPFrom - 1, player.getMasterLevel(skillSPFrom));
                        player.changeSkillLevel(skillSPTo, curLevel + 1, player.getMasterLevel(skillSPTo));
                    }
                    MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);                                        
                } else {
                    List<Pair<MapleStat, Integer>> statupdate = new ArrayList<Pair<MapleStat, Integer>>(2);
                    int APTo = slea.readInt();
                    int APFrom = slea.readInt();
                    switch (APTo) {
                        case 64: // str
                            break;
                        case 128: // dex
                            break;
                        case 256: // int
                            break;
                        case 512: // luk
                            break;
                        case 2048:
                            break;
                        case 8192:
                            break;
                        default: // TODO: implement hp and mp adding
                            c.getSession().write(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true));
                            return;
                    }
                    switch (APFrom) {
                        case 64: // str
                            if (c.getPlayer().getStr() <= 4) return;
                            c.getPlayer().setStr(c.getPlayer().getStr() - 1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.STR, c.getPlayer().getStr()));
                            break;
                        case 128: // dex
                            if (c.getPlayer().getDex() <= 4) return;
                            c.getPlayer().setDex(c.getPlayer().getDex() - 1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.DEX, c.getPlayer().getDex()));
                            break;
                        case 256: // int
                            if (c.getPlayer().getInt() <= 4) return;
                            c.getPlayer().setInt(c.getPlayer().getInt() - 1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.INT, c.getPlayer().getInt()));
                            break;
                        case 512: // luk
                            if (c.getPlayer().getLuk() <= 4) return;
                            c.getPlayer().setLuk(c.getPlayer().getLuk() - 1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.LUK, c.getPlayer().getLuk()));
                            break;
                        case 2048:
                            if (c.getPlayer().getHpApUsed() == 0) return;
                            c.getPlayer().setHpApUsed(c.getPlayer().getHpApUsed() - 1);
                            c.getPlayer().setMaxHp(c.getPlayer().getMaxHp() - hpmin);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.MAXHP, c.getPlayer().getMaxHp()));
                            break;
                        case 8192:
                            if (c.getPlayer().getMpApUsed() == 0) return;
                            c.getPlayer().setMpApUsed(c.getPlayer().getMpApUsed() - 1);
                            c.getPlayer().setMaxMp(c.getPlayer().getMaxMp() - mpmin);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.MAXMP, c.getPlayer().getMaxMp()));
                            break;
                        default:
                            c.getSession().write(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true));
                            return;
                    }
                    switch (APTo) {
                        case 64: // str
                            if (c.getPlayer().getStr() >= 32767) return;
                            c.getPlayer().setStr(c.getPlayer().getStr() + 1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.STR, c.getPlayer().getStr()));
                            break;
                        case 128: // dex
                            if (c.getPlayer().getDex() >= 32767) return;
                            c.getPlayer().setDex(c.getPlayer().getDex() + 1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.DEX, c.getPlayer().getDex()));
                            break;
                        case 256: // int
                            if (c.getPlayer().getInt() >= 32767) return;
                            c.getPlayer().setInt(c.getPlayer().getInt() + 1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.INT, c.getPlayer().getInt()));
                            break;
                        case 512: // luk
                            if (c.getPlayer().getLuk() >= 32767) return;
                            c.getPlayer().setLuk(c.getPlayer().getLuk() + 1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.LUK, c.getPlayer().getLuk()));
                            break;
                        case 2048: // hp
                            if (c.getPlayer().getHpApUsed() >= 30000 || c.getPlayer().getMaxHp() >= 30000) return;
                            if ((c.getPlayer().getMaxHp() + add + HPSkillAdd) > 30000) {
                                c.getPlayer().setMaxHp(30000);
                            } else {
                                c.getPlayer().setMaxHp(c.getPlayer().getMaxHp() + add + HPSkillAdd);
                            }
                            c.getPlayer().setHpApUsed(c.getPlayer().getHpApUsed() + 1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.MAXHP, c.getPlayer().getMaxHp()));
                            break;
                        case 8192: // mp
                            if (c.getPlayer().getMpApUsed() >= 30000 || c.getPlayer().getMaxMp() >= 30000) return;
                            if ((c.getPlayer().getMaxMp() + addmp) > 30000) {
                                c.getPlayer().setMaxMp(30000);
                            } else {
                                c.getPlayer().setMaxMp(c.getPlayer().getMaxMp() + addmp);
                            }
                            c.getPlayer().setMpApUsed(c.getPlayer().getMpApUsed()+1);
                            statupdate.add(new Pair<MapleStat, Integer>(MapleStat.MAXMP, c.getPlayer().getMaxMp()));
                            break;
                        default:
                            c.getSession().write(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true));
                            return;
                    }
                    MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
                    c.getSession().write(MaplePacketCreator.updatePlayerStats(statupdate, true));
                }
            } else if(itemType == 537) {
                String text = slea.readMapleAsciiString();
                slea.readInt();
                c.getPlayer().setChalkboard(new MapleChalkboard(text)); 
                c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.useChalkBoard(c.getPlayer().getId(), text, 1));
                c.getSession().write(MaplePacketCreator.enableActions());
            } else if(itemType == 508) {  
                String text = slea.readAsciiString((int)slea.available() - 4);
                slea.readInt(); //Random
                MaplePacket kite = MaplePacketCreator.useKite(c, text, itemId, text);
                c.getPlayer().getMap().broadcastMessage(kite);
                c.getSession().write(MaplePacketCreator.enableActions());
            } else if(itemType == 504){
                slea.readByte();
                String name = slea.readMapleAsciiString();
                MapleCharacter victim = cserv.getPlayerStorage().getCharacterByName(name);
                if (victim != null) {
                    MapleMap target = victim.getMap();
                    WorldLocation loc = c.getChannelServer().getWorldInterface().getLocation(name);
                    if (loc.map != 180000000 && loc.map >= 100000000) {
                        if (!victim.isHidden() && !victim.isGM() && victim.getWorld() == c.getPlayer().getWorld()) {
                            c.getPlayer().changeMap(target, target.findClosestSpawnpoint(victim.getPosition()));
                        } else {
                            c.getPlayer().dropMessage("Player could not be found in this world.");
                            c.getSession().write(MaplePacketCreator.enableActions());
                        }
                    } else {
                        c.getPlayer().dropMessage("You cannot teleport to this map.");
                        c.getSession().write(MaplePacketCreator.enableActions());
                    }
                } else {
                    victim = c.getPlayer();
                    boolean shouldwarp = false;
                    for (int i : WorldRegistryImpl.getInstance().getChannelServer()) {
                        ChannelWorldInterface cwi = WorldRegistryImpl.getInstance().getChannel(i);
                        if (cwi.isConnected(name)) {
                            if (cwi.getWorldId() == c.getPlayer().getWorld()) {
                                shouldwarp = true;
                            }
                        }
                    }
                    if (!shouldwarp) {
                        c.getPlayer().dropMessage("Player could not be found in this world.");
                        c.getSession().write(MaplePacketCreator.enableActions());
                        return;
                    }
                    WorldLocation loc = c.getChannelServer().getWorldInterface().getLocation(name);
                    if (loc != null) {
                        if (loc.map != 180000000 && loc.map >= 100000000) {
                            if (!victim.isHidden() && !victim.isGM() && victim.getWorld() == c.getPlayer().getWorld()) {
                                MapleMap target = c.getChannelServer().getMapFactory().getMap(loc.map, c.getPlayer().getWorld());
                                c.getPlayer().cancelAllBuffs();
                                String ip = c.getChannelServer().getIP(loc.channel);
                                c.getPlayer().getMap().removePlayer(c.getPlayer());
                                victim.setMap(target);
                                String[] socket = ip.split(":");
                                if (c.getPlayer().getTrade() != null) {
                                    MapleTrade.cancelTrade(c.getPlayer());
                                }
                                c.getPlayer().saveToDB(true);
                                if (c.getPlayer().getCheatTracker() != null) c.getPlayer().getCheatTracker().dispose();
                                ChannelServer.getInstance(c.getChannel()).removePlayer(c.getPlayer());
                                c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
                                try {
                                    c.getSession().write(MaplePacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1])));
                                } catch (Exception e) {
                                    throw new RuntimeException(e);
                                }
                            } else {
                                c.getPlayer().dropMessage("You cannot teleport to this map or world.");
                                c.getSession().write(MaplePacketCreator.enableActions());
                            }
                        } else {
                            c.getPlayer().dropMessage("You cannot teleport to this map.");
                            c.getSession().write(MaplePacketCreator.enableActions());
                        }
                    } else {
                        c.getPlayer().dropMessage("Player could not be found in this world.");
                        c.getSession().write(MaplePacketCreator.enableActions());
                    }
                }
            } else if (itemType == 528) {
                if (itemId == 5281000) {
                    //poison mist without poison o_o
                    Rectangle bounds = new Rectangle((int)c.getPlayer().getPosition().getX(), (int)c.getPlayer().getPosition().getY(), 1, 1);
                    MapleStatEffect mse = new MapleStatEffect();
                    mse.setSourceId(2111003);
                    MapleMist mist = new MapleMist(bounds, c.getPlayer(), mse);
                    c.getPlayer().setMist(mist);
                    c.getPlayer().getMap().spawnMist(mist, 10000, false, true);
                    c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.getChatText(c.getPlayer().getId(), "Oh no, I farted!", false, 1));
                } else {
                    MapleMist mist = c.getPlayer().getMist();
                    c.getPlayer().getMap().removeMapObject(mist);
                    c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.getChatText(c.getPlayer().getId(), "That was smelly, Gladly i removed my fart with floral scent!", false, 1));
                }
            } else if (itemType == 546) {
                c.getPlayer().changeSkillLevel(SkillFactory.getSkill(8), 1, 1);
                MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
            } else if (itemType == 545) {
                int bla = slea.readInt();
                MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, itemId, 1, true, false);
            } else {
                log.warn("unhandled cash item used ({})", itemType);
            }
		} catch (RemoteException e) {
			c.getChannelServer().reconnectWorld();
			log.error("REMOTE ERROR", e);
		}
	}
}